﻿//---------------------------------------------------------------------
// <copyright file="EFOracleDataReader.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//---------------------------------------------------------------------

namespace EFOracleOdpProvider
{
    using System;
    using System.Data;
    using System.Data.Common;
    using System.Data.Metadata.Edm;
    using System.Globalization;

    public partial class EFOracleOdpDataReader : DbDataReader
    {
        #region Private Fields

        private DbDataReader _oracleReader = null;
        private EFOracleOdpCommand _command;
        private PrimitiveType[] _expectedColumnTypes;

        #endregion

        #region Constructor

        internal EFOracleOdpDataReader(EFOracleOdpCommand command, DbDataReader wrappedReader, PrimitiveType[] expectedColumnTypes)
        {
            _command = command;
            _oracleReader = wrappedReader;
            _expectedColumnTypes = expectedColumnTypes;
        }

        protected override void Dispose(bool disposing)
        {
            GC.SuppressFinalize(this);

            if (disposing)
            {
                _oracleReader.Dispose();
            }
            base.Dispose(disposing);
        }

        #endregion

        #region Properties

        public override int Depth
        {
            get { return _oracleReader.Depth; }
        }

        public override int FieldCount
        {
            get { return _oracleReader.FieldCount; }
        }

        public override bool HasRows
        {
            get { return _oracleReader.HasRows; }
        }

        public override bool IsClosed
        {
            get { return _oracleReader.IsClosed; }
        }

        public override int RecordsAffected
        {
            get { return _oracleReader.RecordsAffected; }
        }

        public override object this[string name]
        {
            get
            {
                // not very fast, but we keep the type conversion logic in GetValue()
                return GetValue(GetOrdinal(name));
            }
        }

        public override object this[int ordinal]
        {
            get { return GetValue(ordinal); }
        }

        #endregion

        #region Methods

        public override void Close()
        {
            GC.SuppressFinalize(this);
            _oracleReader.Close();
        }

        public override System.Runtime.Remoting.ObjRef CreateObjRef(Type requestedType)
        {
            return _oracleReader.CreateObjRef(requestedType);
        }

        public override bool Equals(object obj)
        {
            return _oracleReader.Equals(((EFOracleOdpDataReader)obj)._oracleReader);
        }

        public override bool GetBoolean(int ordinal)
        {
            return (bool)GetValue(ordinal);
        }

        public override byte GetByte(int ordinal)
        {
            return (byte)GetValue(ordinal);
        }

        public override long GetBytes(int ordinal, long dataOffset, byte[] buffer, int bufferOffset, int length)
        {
            return _oracleReader.GetBytes(ordinal, dataOffset, buffer, bufferOffset, length);
        }

        public override char GetChar(int ordinal)
        {
            return (char)GetValue(ordinal);
        }

        public override long GetChars(int ordinal, long dataOffset, char[] buffer, int bufferOffset, int length)
        {
            return _oracleReader.GetChars(ordinal, dataOffset, buffer, bufferOffset, length);
        }

        public override string GetDataTypeName(int ordinal)
        {
            if (_expectedColumnTypes != null)
            {
                return _expectedColumnTypes[ordinal].Name;
            }
            return _oracleReader.GetDataTypeName(ordinal);
        }

        public override DateTime GetDateTime(int ordinal)
        {
            return (DateTime)GetValue(ordinal);
        }

        public override decimal GetDecimal(int ordinal)
        {
            return (decimal)GetValue(ordinal);
        }

        public override double GetDouble(int ordinal)
        {
            return (double)GetValue(ordinal);
        }

        public override System.Collections.IEnumerator GetEnumerator()
        {
            return _oracleReader.GetEnumerator();
        }

        public override Type GetFieldType(int ordinal)
        {
            if (_expectedColumnTypes != null)
            {
                return _expectedColumnTypes[ordinal].ClrEquivalentType;
            }
            return _oracleReader.GetFieldType(ordinal);
        }

        public override float GetFloat(int ordinal)
        {
            return (float)GetValue(ordinal);
        }

        public override Guid GetGuid(int ordinal)
        {
            return (Guid)GetValue(ordinal);
        }

        public override int GetHashCode()
        {
            return _oracleReader.GetHashCode();
        }

        public override short GetInt16(int ordinal)
        {
            return (short)GetValue(ordinal);
        }

        public override int GetInt32(int ordinal)
        {
            return (int)GetValue(ordinal);
        }

        public override long GetInt64(int ordinal)
        {
            return (long)GetValue(ordinal);
        }

        public override string GetName(int ordinal)
        {
            return _oracleReader.GetName(ordinal);
        }

        public override int GetOrdinal(string name)
        {
            return _oracleReader.GetOrdinal(name);
        }

        public override DataTable GetSchemaTable()
        {
            return _oracleReader.GetSchemaTable();
        }

        public override string GetString(int ordinal)
        {
            return (string)GetValue(ordinal);
        }

        public override object GetValue(int ordinal)
        {
            object rawValue = _oracleReader.GetValue(ordinal);

            if (_expectedColumnTypes != null)
            {
                if (!(rawValue is DBNull) && rawValue.GetType()
                    != _expectedColumnTypes[ordinal].ClrEquivalentType)
                {
                    rawValue = ChangeType(rawValue, _expectedColumnTypes[ordinal].ClrEquivalentType);
                }
            }
            return rawValue;
        }

        private object ChangeType(object sourceValue, Type targetType)
        {
            if (sourceValue is byte[] && targetType == typeof(Guid))
            {
                return new Guid((byte[])sourceValue);
            }

            if (sourceValue is DateTime && targetType == typeof(DateTimeOffset))
            {
                return new DateTimeOffset((DateTime)sourceValue);
            }

            return Convert.ChangeType(sourceValue, targetType, CultureInfo.InvariantCulture);
        }

        public override int GetValues(object[] values)
        {
            for (int i = 0; i < values.Length; ++i)
            {
                values[i] = GetValue(i);
            }
            return values.Length;
        }

        public override bool IsDBNull(int ordinal)
        {
            return _oracleReader.IsDBNull(ordinal);
        }

        public override bool NextResult()
        {
            return _oracleReader.NextResult();
        }

        public override bool Read()
        {
            return _oracleReader.Read();
        }

        #endregion
    }
}
